package tv.dyndns.kishibe.server.relevance;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

import tv.dyndns.kishibe.server.QMACloneModule;
import tv.dyndns.kishibe.server.util.Normalizer;

import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Guice;
import com.google.inject.Inject;

public class Dictionary {
	private static final String wikipediaTitleListFilePath = "/var/www/qmaclone/jawiki-latest-all-titles-in-ns0.gz";
	public static final String filePath = "/var/www/qmaclone/jawiki-latest-all-titles-in-ns0.dat";
	private final Downloader downloader;

	@Inject
	public Dictionary(Downloader downloader) {
		this.downloader = downloader;
	}

	public void create() throws FileNotFoundException, IOException {
		final Set<String> words = new HashSet<String>();
		addWikipediaTitleList(words);
		addNicoDicTitleList(words);
		final Trie trie = Trie.create(words);
		trie.save(filePath);

		final List<String> wordList = new ArrayList<String>(words);

		final List<Integer> extractedWordIndexes = new ArrayList<Integer>();
		trie.parse(
				"少年時代から無鉄砲な江戸っ子の坊っちゃんと、肉親から疎んじられる彼に無償の愛を注ぐ女中である清の描写から『坊っちゃん』の物語は幕を開く。坊っちゃんは両親と死別後、清とも離れ、四国の旧制中学校に数学の教師として赴任する。着任早々、校長には狸、教頭には赤シャツ、画学の教師には野だいこ、英語の教師にはうらなり、数学の主任教師には山嵐と、それぞれにあだ名を付けた。坊っちゃんは授業の時に生徒達から、てんぷらそばを四杯食べた件等の私事について執拗に冷やかされる。また初めての宿直の夜には、寄宿生達から蒲団の中に大量のバッタ（厳密にはイナゴ）を入れられる等の嫌がらせを受け、激怒して、何としても犯人を突き止めようとしたため、大事になってしまう。坊っちゃんは赤シャツとその腰巾着である野だいこから、生徒による嫌がらせは山嵐の扇動によるものであると婉曲的に吹き込まれ、一時は真に受けてしまう。しかし、後日の職員会議において、先の寄宿生の不祥事に坊っちゃんが毅然とした措置を主張したところ、狸をはじめとする事なかれ主義の職員達は取り合ってくれなかったのに対し、山嵐だけが坊っちゃんを支持してくれた。お互いに対する誤解は解けていき、坊っちゃんと山嵐とは、かえって強い友情で結ばれるようになる。うらなりには、マドンナとあだ名される婚約者がいたが、赤シャツがマドンナへの横恋慕から、お人好しのうらなりを体良く延岡に左遷したという事実を知り、坊っちゃんは義憤にかられる。実は山嵐も、赤シャツの横恋慕を糾弾したため、逆恨みされていたのであった。日露戦争の祝勝会の日に、坊っちゃんと山嵐は赤シャツの謀略により、中学校と師範学校の生徒同士の乱闘騒ぎに巻き込まれた上、いわれ無き生徒扇動の罪を着せられ、山嵐が辞職に追い込まれる。卑劣な仕打ちに憤激した坊っちゃんと山嵐は、赤シャツと野だいこの不祥事を暴くための監視を始め、ついに芸者遊び帰りの赤シャツと野だいこを取り押さえる。そして芸者遊びについて詰問するも、しらを切られたため、業を煮やし、激しく暴行を加えた。即刻辞職した坊っちゃんは、帰郷後、街鉄（現在の都電）の技手となって、再び、清と同居生活を始めるが、清が亡くなり、遺言通り小日向の養源寺に葬った事を記して、『坊っちゃん』の物語は幕を閉じる。",
				extractedWordIndexes, null, null);
		for (int wordIndex : extractedWordIndexes) {
			System.out.println(wordIndex + " " + wordList.get(wordIndex));
		}
	}

	public void addWikipediaTitleList(final Set<String> words) throws FileNotFoundException,
			IOException {
		// 日本語版Wikipediaタイトル一覧
		final Scanner scanner = new Scanner(new BufferedInputStream(new GZIPInputStream(
				new BufferedInputStream(new FileInputStream(wikipediaTitleListFilePath)))), "utf-8");
		while (scanner.hasNextLine()) {
			String line = scanner.nextLine().replaceAll("\n", "");
			if (line.isEmpty()) {
				continue;
			}

			if (line.contains("(")) {
				line = line.substring(0, line.indexOf("("));
			}

			line = line.replaceAll("_", "");

			words.add(Normalizer.normalize(line));

			if (words.size() % 10000 == 0) {
				System.out.println("Wikipedia: " + words.size());
			}
		}
		scanner.close();
	}

	public void addNicoDicTitleList(Set<String> words) {
		ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors() * 2);

		// ニコニコ大百科
		Set<String> visited = Sets.newHashSet();
		String initialUrl = "http://dic.nicovideo.jp/m/a/a";
		visited.add(initialUrl);
		NicoDicDownloader downloader = new NicoDicDownloader(initialUrl,
				Collections.synchronizedSet(visited), Collections.synchronizedSet(words),
				new AtomicInteger(), new AtomicInteger());
		pool.invoke(downloader);
		pool.shutdown();
	}

	public static void main(String[] args) throws Exception {
		Guice.createInjector(new QMACloneModule()).getInstance(Dictionary.class).create();
	}

	private static final Pattern PATTERN_LINK = Pattern.compile("<a href=\"(/m/yp/.+?)\">");
	private static final Pattern PATTERN_WORD = Pattern.compile("<a href=\"/a/.+?\">(.+?)</a>");

	@SuppressWarnings("serial")
	private class NicoDicDownloader extends RecursiveAction {
		private final String url;
		private final Set<String> visited;
		private final Set<String> words;
		private final AtomicInteger completedTaskCount;
		private final AtomicInteger remainingTaskCount;

		public NicoDicDownloader(String url, Set<String> visited, Set<String> words,
				AtomicInteger completedTaskCount, AtomicInteger remainingTaskCount) {
			this.url = url;
			this.visited = visited;
			this.words = words;
			this.completedTaskCount = completedTaskCount;
			this.remainingTaskCount = remainingTaskCount;
			remainingTaskCount.incrementAndGet();
		}

		@Override
		protected void compute() {
			System.out.println(Objects.toStringHelper(this)
					.add("remainingTaskCount", remainingTaskCount)
					.add("completedTaskCount", completedTaskCount).add("url", url)
					.add("|words|", words.size()).toString());

			String html = null;
			try {
				html = download(url, "utf-8");
			} catch (MalformedURLException e) {
				e.printStackTrace();
				return;
			}

			addLinks(html);

			addWords(html);

			completedTaskCount.incrementAndGet();
		}

		private void addLinks(String html) {
			List<NicoDicDownloader> downloaders = Lists.newArrayList();
			Matcher matcher = PATTERN_LINK.matcher(html);
			while (matcher.find()) {
				String s = matcher.group(1);
				if (s.contains("\"")) {
					s = s.substring(0, s.indexOf("\""));
				}

				if (17 < s.length() && s.charAt(17) == '%') {
					continue;
				}

				String nextUrl = "http://dic.nicovideo.jp" + s;
				if (visited.contains(nextUrl)) {
					continue;
				}

				visited.add(nextUrl);
				NicoDicDownloader downloader = new NicoDicDownloader(nextUrl, visited, words,
						completedTaskCount, remainingTaskCount);
				downloaders.add(downloader);
			}

			invokeAll(downloaders);
		}

		private void addWords(String html) {
			Matcher matcher = PATTERN_WORD.matcher(html);
			while (matcher.find()) {
				String word = matcher.group(1);
				word = word.replaceAll("_", "");

				if (word.contains("（")) {
					word = word.substring(0, word.indexOf('（'));
				}

				if (word.contains("(")) {
					word = word.substring(0, word.indexOf('('));
				}

				word = word.trim();
				word = Normalizer.normalize(word);
				words.add(word);

				int numberOfWords = words.size();
				if (numberOfWords % 10000 == 0) {
					System.out.println("Nico: " + numberOfWords);
				}
			}
		}
	}

	String download(String url, String encoding) throws MalformedURLException {
		return downloader.downloadAsString(new URL(url), encoding);
	}
}
